"""
态势对象知识图谱导出服务
"""
from typing import List, Optional, Dict, Any
from uuid import UUID
from neo4j import GraphDatabase, Driver
from neo4j.exceptions import ServiceUnavailable, AuthError
from sqlalchemy import select, and_
from sqlalchemy.ext.asyncio import AsyncSession

from src.situation.models import SituationObject
from src.situation.relationship_models import SituationRelationship
from src.situation.relationship_schemas import GraphExportRequest, GraphExportResponse
from src.utils import logger


class GraphExportService:
    """知识图谱导出服务"""

    def __init__(self, neo4j_uri: str = "bolt://localhost:7687", neo4j_user: str = "neo4j", neo4j_password: str = "0123456789"):
        """
        初始化 Neo4j 连接

        Args:
            neo4j_uri: Neo4j 连接 URI
            neo4j_user: Neo4j 用户名
            neo4j_password: Neo4j 密码
        """
        self.uri = neo4j_uri
        self.user = neo4j_user
        self.password = neo4j_password
        self.driver: Optional[Driver] = None

    def connect(self):
        """建立 Neo4j 连接"""
        try:
            self.driver = GraphDatabase.driver(self.uri, auth=(self.user, self.password))
            # 测试连接
            with self.driver.session() as session:
                result = session.run("RETURN 1")
                if result.single()[0] == 1:
                    logger.info(f"[知识图谱] Neo4j 连接成功: {self.uri}")
                    return True
        except ServiceUnavailable:
            logger.error(f"[知识图谱] Neo4j 服务不可用: {self.uri}")
            raise ConnectionError("Neo4j 服务不可用,请检查服务是否启动")
        except AuthError:
            logger.error(f"[知识图谱] Neo4j 认证失败")
            raise ConnectionError("Neo4j 认证失败,请检查用户名和密码")
        except Exception as e:
            logger.error(f"[知识图谱] Neo4j 连接失败: {e}")
            raise ConnectionError(f"Neo4j 连接失败: {e}")

    def close(self):
        """关闭 Neo4j 连接"""
        if self.driver:
            self.driver.close()
            logger.info("[知识图谱] Neo4j 连接已关闭")

    async def export_to_neo4j(self, db: AsyncSession, request: GraphExportRequest) -> GraphExportResponse:
        """
        导出态势对象及关系到 Neo4j 知识图谱

        Args:
            db: 数据库会话
            request: 导出请求

        Returns:
            导出结果
        """
        logger.info(f"[知识图谱] 开始导出到 Neo4j, clear_existing={request.clear_existing}")

        errors = []
        objects_exported = 0
        relationships_exported = 0

        try:
            # 1. 连接 Neo4j
            self.connect()

            # 2. 如果需要清除已存在数据
            if request.clear_existing:
                self._clear_existing_data()

            # 3. 查询要导出的对象
            objects = await self._fetch_objects(db, request.object_ids)
            logger.info(f"[知识图谱] 查询到 {len(objects)} 个对象")

            # 4. 导出对象节点
            for obj in objects:
                try:
                    self._create_object_node(obj)
                    objects_exported += 1
                except Exception as e:
                    error_msg = f"导出对象 {obj.name} 失败: {e}"
                    logger.error(f"[知识图谱] {error_msg}")
                    errors.append(error_msg)

            # 5. 导出关系
            if request.include_relationships:
                object_ids = [obj.id for obj in objects]
                relationships = await self._fetch_relationships(db, object_ids)
                logger.info(f"[知识图谱] 查询到 {len(relationships)} 个关系")

                for rel in relationships:
                    try:
                        self._create_relationship(rel)
                        relationships_exported += 1
                    except Exception as e:
                        error_msg = f"导出关系 {rel.id} 失败: {e}"
                        logger.error(f"[知识图谱] {error_msg}")
                        errors.append(error_msg)

            success = len(errors) == 0
            message = f"成功导出 {objects_exported} 个对象, {relationships_exported} 个关系"
            if errors:
                message += f", {len(errors)} 个错误"

            logger.info(f"[知识图谱] 导出完成: {message}")

            return GraphExportResponse(success=success, objects_exported=objects_exported, relationships_exported=relationships_exported, errors=errors, message=message)

        except Exception as e:
            logger.error(f"[知识图谱] 导出失败: {e}")
            return GraphExportResponse(success=False, objects_exported=objects_exported, relationships_exported=relationships_exported, errors=[str(e)], message=f"导出失败: {e}")
        finally:
            self.close()

    def _clear_existing_data(self):
        """清除 Neo4j 中已存在的态势对象数据"""
        logger.info("[知识图谱] 清除已存在的数据")
        with self.driver.session() as session:
            # 删除所有Entity节点及其关系（包含态势对象数据）
            session.run("MATCH (n:Entity) DETACH DELETE n")

    async def _fetch_objects(self, db: AsyncSession, object_ids: Optional[List[UUID]] = None) -> List[SituationObject]:
        """查询要导出的对象"""
        conditions = [SituationObject.is_deleted == False]  # noqa: E712

        if object_ids:
            conditions.append(SituationObject.id.in_(object_ids))

        stmt = select(SituationObject).where(and_(*conditions))
        result = await db.execute(stmt)
        return result.scalars().all()

    async def _fetch_relationships(self, db: AsyncSession, object_ids: List[UUID]) -> List[SituationRelationship]:
        """查询要导出的关系"""
        stmt = select(SituationRelationship).where(
            and_(SituationRelationship.source_object_id.in_(object_ids), SituationRelationship.target_object_id.in_(object_ids), SituationRelationship.is_deleted == False)  # noqa: E712
        )
        result = await db.execute(stmt)
        return result.scalars().all()

    def _create_object_node(self, obj: SituationObject):
        """创建对象节点 - 调整为默认查询格式"""
        with self.driver.session() as session:
            # 准备节点属性 - 兼容默认查询格式
            properties = {
                "id": str(obj.id),
                "name": obj.name,
                # 保留原始态势对象属性
                "object_type": obj.object_type,
                "equipment_category": obj.equipment_category or "",  # 添加装备类别字段
                "code": obj.code,
                "model": obj.model or "",
                "organization": obj.organization or "",
                "deployment_status": obj.deployment_status or "",
                "created_at": obj.created_at.isoformat() if obj.created_at else "",
                # 添加其他有用字段
                "description": obj.description or "",
                "detection_range": float(obj.detection_range) if obj.detection_range else None,
                "response_time": float(obj.response_time) if obj.response_time else None,
                "accuracy": float(obj.accuracy) if obj.accuracy else None,
                "location": obj.location or "",
            }

            # 使用Entity标签而非SituationObject，以兼容默认查询
            cypher = """
            MERGE (n:Entity {id: $id})
            SET n += $properties
            """
            session.run(cypher, id=str(obj.id), properties=properties)
            logger.info(f"[知识图谱] 创建节点: {obj.name} ({obj.code})")

    def _create_relationship(self, rel: SituationRelationship):
        """创建关系 - 调整为默认查询格式"""
        with self.driver.session() as session:
            # 获取关系类型的中文名称
            relationship_chinese_name = self._get_relationship_chinese_name(rel.relationship_type)

            # 创建关系属性 - 包含原始关系类型和中文名称
            properties = {
                "type": relationship_chinese_name,  # 默认查询期望的type属性，存储中文名称
                "source_id": str(rel.source_object_id),  # 默认查询期望的source_id
                "target_id": str(rel.target_object_id),   # 默认查询期望的target_id
                "relationship_level": rel.relationship_level,
                "description": rel.description or "",
                "created_at": rel.created_at.isoformat() if rel.created_at else "",
                # 保留原始关系类型信息
                "original_type": rel.relationship_type,
            }

            # 统一使用RELATION关系类型，以兼容默认查询
            cypher = """
            MATCH (source:Entity {id: $source_id})
            MATCH (target:Entity {id: $target_id})
            MERGE (source)-[r:RELATION]->(target)
            SET r += $properties
            """

            session.run(cypher, source_id=str(rel.source_object_id), target_id=str(rel.target_object_id), properties=properties)
            logger.info(f"[知识图谱] 创建关系: {rel.source_object_id} -[{relationship_chinese_name}]-> {rel.target_object_id}")

    def _get_relationship_chinese_name(self, relationship_type: str) -> str:
        """获取关系类型的中文名称"""
        relationship_mapping = {
            # 指挥隶属关系
            "subordinate_to": "隶属于",
            "command": "指挥",
            "jurisdiction": "管辖",

            # 部署与位置关系
            "deployed_at": "部署在",
            "stationed_at": "驻扎",
            "patrols": "巡逻",
            "docked_at": "停靠",

            # 支援与协同关系
            "supports": "支援",
            "coordinates_with": "协同",
            "attached_to": "配属",
            "cooperates_with": "配合",

            # 保障与补给关系
            "supplies": "补给",
            "maintains": "维修",
            "transports": "运输",
            "provides_logistics": "后勤保障",

            # 观测与侦察关系
            "monitors": "监视",
            "reconnaissance": "侦察",
            "tracks": "跟踪",
            "observes": "观察",

            # 目标与观测能力关系
            "detects": "探测到",
            "tracks_target": "跟踪目标",
            "identifies": "识别",
            "covers": "覆盖",

            # 防御与防护关系
            "protects": "保护",
            "guards": "警戒",
            "intercepts": "拦截",
            "provides_cover": "掩护",

            # 通信与数据关系
            "communicates_with": "通信",
            "data_link": "数据链",
            "command_chain": "指挥链",
            "intelligence_share": "情报共享",

            # 威胁与对抗关系
            "threatens": "威胁",
            "opposes": "对抗",
            "jams": "干扰",
            "suppresses": "压制",

            # 特定装备关系
            "carries": "搭载",
            "formation": "编队",
            "escorts": "护航",
            "guides": "引导",

            # 地理与基础设施关系
            "connects": "连接",
            "depends_on": "依赖",
            "controls": "控制",
            "influences": "影响",
        }

        return relationship_mapping.get(relationship_type, relationship_type)
